#include "ET++.ph"
#ifdef __GNUG__
#pragma implementation
#endif

#include "EtLook.h"

#include "Env.h"
#include "WindowSystem.h"
#include "Box.h"
#include "Buttons.h"
#include "Layout.h"
#include "Math.h"

static u_short UpArrowBits[]= {
#   include "UpArrow.image"
};
static u_short InvUpArrowBits[]= {
#   include "UpArrowInv.image"
};
static u_short DownArrowBits[]= {
#   include "DownArrow.image"
};
static u_short InvDownArrowBits[]= {
#   include "DownArrowInv.image"
};
static u_short LeftArrowBits[]= {
#   include "LeftArrow.image"
};
static u_short InvLeftArrowBits[]= {
#   include "LeftArrowInv.image"
};
static u_short RightArrowBits[]= {
#   include "RightArrow.image"
};
static u_short InvRightArrowBits[]= {
#   include "RightArrowInv.image"
};
static u_short menuArrowBits[]= {
#   include "menuarrow.image"
};
static u_short RadioButtonOnBits[]= {
#   include "RadioButtonOn.image"
};
static u_short RadioButtonOffBits[]= {
#   include "RadioButtonOff.image"
};
static u_short RadioButtonFeedbackBits[]= {
#   include "RadioButtonFeedback.image"
};
static u_short CheckMarkOnBits[]= {
#   include "CheckmarkOn.image"
};
static u_short CheckMarkOffBits[]= {
#   include "CheckmarkOff.image"
};
static u_short CheckMarkFeedbackBits[]= {
#   include "CheckmarkInv.image"
};
static u_short CheckMarkBits[]= {
#   include "Checkmark.image"
};
static u_short SliderUpBits[]= {
#   include "SliderButtonUp.image"
};
static u_short SliderDownBits[]= {
#   include "SliderButtonDown.image"
};
static u_short SliderUpInvBits[]= {
#   include "SliderButtonUpInv.image"
};
static u_short SliderDownInvBits[]= {
#   include "SliderButtonDownInv.image"
};

//---- ETLook ------------------------------------------------------------------

ETLook *gET;

Look *MakeEtLook()
{
	return new ETLook;
}

//---- ETMenuBorderLayout ------------------------------------------------------

class ETMenuBorderLayout : public Layout {
	int shadow;
public:
	ETMenuBorderLayout(int shad)
		{ shadow= shad; }
	void SetOrigin(VObject*, const Point&);
	void SetExtent(VObject*, const Point&);
	Metric GetMinSize(VObject*);
	void Adorn(VObject*, const Rectangle&, int code);
};

Metric ETMenuBorderLayout::GetMinSize(VObject *acv)
{
	Point e(acv->At(0)->GetMinSize().extent + (4+shadow));
	if (acv->Size() > 1) {
		Metric tm= acv->At(1)->GetMinSize();
		e.x= Math::Max(e.x, tm.extent.x);
		e.y+= tm.extent.y;
	}
	return e;
}

void ETMenuBorderLayout::SetExtent(VObject *acv, const Point &ee)
{
	Point e(ee - (4+shadow));
	if (acv->Size() > 1) {
		acv->At(1)->CalcExtent();
		e.y-= acv->At(1)->Height();
	}
	acv->At(0)->SetExtent(e);
}

void ETMenuBorderLayout::SetOrigin(VObject *acv, const Point &a)
{
	Point at(a+gPoint2);
	if (acv->Size() > 1) {
		int x= (acv->Width()-(4+shadow)-acv->At(1)->Width())/2;
		acv->At(1)->SetOrigin(Point(at.x+x, at.y));
		at.y+= acv->At(1)->Height();
	}
	acv->At(0)->SetOrigin(at);
}

void ETMenuBorderLayout::Adorn(VObject *acv, const Rectangle &r, int)
{
	Rectangle rr(acv->contentRect);

	rr.extent-= shadow;

	GrPaintRect(rr, gET->backgroundColor);
	if (! acv->At(0)->contentRect.ContainsRect(r)) {
		Ink *pat= shadow == 1 ? ePatBlack : ePatGrey50;
		GrPaintRect(Rectangle(rr.origin.x+rr.extent.x, rr.origin.y+shadow, shadow, rr.extent.y), pat);
		GrPaintRect(Rectangle(rr.origin.x+shadow, rr.origin.y+rr.extent.y, rr.extent.x, shadow), pat);

		GrSetPenNormal();
		GrStrokeRect(rr);
		if (acv->Size() > 1) {
			Rectangle cr(r);
			cr.extent-= shadow;
			cr.extent.y= acv->At(1)->Height();
			cr= cr.Inset(2);
			if (GrHasColor())
				GrPaintRect(cr, gET->menuTitleColor);
			acv->At(1)->DrawAll(r);
			if (!GrHasColor())
				GrPaintRect(cr, gET->menuTitleColor);
		}
	}
	acv->At(0)->DrawAll(r);
}

//---- ETMenuLineLayout --------------------------------------------------------

class ETMenuLineLayout : public Layout {
public:
	ETMenuLineLayout()
		{ }
	Metric GetMinSize(VObject*)
		{ return gPoint1; }
	void Adorn(VObject *acv, const Rectangle&, int)
		{ GrPaintRect(acv->contentRect, ePatGrey50); }
};

//---- ETScrollButtonLayout ----------------------------------------------------

class ETScrollButtonLayout : public Layout {
public:
	ETScrollButtonLayout()
		{ }
	Metric GetMinSize(VObject*)
		{ return 16 + gPoint4; }
	void Adorn(VObject *acv, const Rectangle &r, int code);
};

void ETScrollButtonLayout::Adorn(VObject*, const Rectangle &r, int code)
{
	GrShowBitMap(r.Inset(2), gET->arrows[code & 0x03]);
	if (TESTBIT(code, 4))
		GrPaintBitMap(r.Inset(2), gET->invarrows[code & 0x03], gHighlightColor);
}

//---- ETToggleButtonLayout ----------------------------------------------------

class ETToggleButtonLayout : public Layout {
	Bitmap *on, *off, *highlight;
public:
	ETToggleButtonLayout(Bitmap *b1, Bitmap *b2, Bitmap *b3)
		{ on= b1; off= b2; highlight= b3; }
	Metric GetMinSize(VObject *acv);
	void Adorn(VObject *acv, const Rectangle &r, int code);
	void SetOrigin(VObject *acv, const Point &at);
};

Metric ETToggleButtonLayout::GetMinSize(VObject *acv)
{
	Metric m(on->Size(), 12);
	if (acv->Size() > 0)
		m= HCat(m, acv->At(0)->GetMinSize(), 6);
	return m;
}

void ETToggleButtonLayout::SetOrigin(VObject *acv, const Point &a)
{
	if (acv->Size() > 0) {
		Point at(a);
		at.x+= on->Size().x+6;
		acv->At(0)->SetOrigin(at);
	}
}

void ETToggleButtonLayout::Adorn(VObject *acv, const Rectangle &r, int code)
{
	Rectangle rr(acv->GetOrigin(), on->Size());

	if (acv->Size() > 0) {
		acv->At(0)->DrawAll(r);
		rr.origin.y+= GetMinSize(acv).base - 12;
	}

	if (TESTBIT(code, 3))
		GrPaintBitMap(rr, highlight, gHighlightColor);
	GrPaintBitMap(rr, TESTBIT(code, 1) ? on : off,
					TESTBIT(code, 2) ? ePatBlack : gET->disableInk);
}

//---- ETMenuItemLayout ------------------------------------------------------

class ETMenuItemLayout : public Layout {
public:
	ETMenuItemLayout()
		{ }
	Metric GetMinSize(VObject *acv);
	void SetOrigin(VObject *acv, const Point &at);
	void Adorn(VObject *acv, const Rectangle &r, int code);
};

Metric ETMenuItemLayout::GetMinSize(VObject *acv)
{
	Metric m= acv->At(0)->GetMinSize();
	m.extent.x+= 2+gET->CheckMark->Size().x+4+11;
	return m;
}

void ETMenuItemLayout::SetOrigin(VObject *acv, const Point &a)
{
	if (acv->Size() > 0) {
		Point at(a);
		at.x+= 2+gET->CheckMark->Size().x+4;
		acv->At(0)->SetOrigin(at);
	}
}

void ETMenuItemLayout::Adorn(VObject *acv, const Rectangle &r, int code)
{
	Rectangle cr(acv->contentRect),
			rr(cr.origin, gET->CheckMark->Size());

	Ink *ink= TESTBIT(code, 5) ? ePatBlack : gET->disableInk;

	if (TESTBIT(code, 3) && GrHasColor())
		GrPaintRect(cr, gHighlightColor);

	if (acv->Size() > 0)
		acv->At(0)->DrawAll(r);
		
	if (TESTBIT(code, 4)) {
		Rectangle rr(gET->menuArrow->Size());
		rr.origin= cr.NE()-Point(11,-4);
		GrPaintBitMap(rr, gET->menuArrow, ink);
	}

	if ((code & 3) && TESTBIT(code, 2)) {
		rr.origin.x+= 2;
		rr.origin.y+= (cr.extent.y-gET->CheckMark->Size().y)/2;
		GrPaintBitMap(rr, gET->CheckMark, ink);
	}

	if (TESTBIT(code, 3) && !GrHasColor())
		GrPaintRect(cr, gHighlightColor);
}

//---- ETScrollBarLayout -------------------------------------------------------

class ETScrollBarLayout : public Layout {
public:
	ETScrollBarLayout()
		{ }
	void Adorn(VObject *acv, const Rectangle &r, int code);
};

void ETScrollBarLayout::Adorn(VObject *acv, const Rectangle &r, int)
{
	((Expander*)acv)->Expander::Draw(r);
	GrSetPenNormal();
	GrStrokeRect(r);
	GrSetPenNormal();
}

//---- ETActionButtonLayout ----------------------------------------------------

class ETActionButtonLayout : public Layout {
	Point border;
	bool dflt;
public:
	ETActionButtonLayout(bool d)
		{ dflt= d; }
	void Adorn(VObject *acv, const Rectangle &r, int code);
	Metric GetMinSize(VObject *acv)
		{ return acv->At(0)->GetMinSize().Expand(gPoint6); }
};

void ETActionButtonLayout::Adorn(VObject *acv, const Rectangle &r, int code)
{
	Rectangle rr(r.Inset(4));
	VObject *vop= acv->At(0);

	if (! TESTBIT(code, 2))
		GrSetPenInk(gET->disableInk);

	if (GrHasColor() && (TESTBIT(code, 1) || TESTBIT(code, 3)))
		GrPaintRoundRect(rr, 14, gHighlightColor);
		
	vop->DrawAll(r);

	if (!GrHasColor() && (TESTBIT(code, 1) || TESTBIT(code, 3)))
		GrPaintRoundRect(rr, 14, gHighlightColor);

	GrStrokeRoundRect(rr, 14);
	if (dflt) {
		GrSetPenSize(2);
		GrStrokeRoundRect(r, 18);
	}
	GrSetPenNormal();
}
		
//---- ETPopupButtonLayout -----------------------------------------------------

class ETPopupButtonLayout : public Layout {
public:
	ETPopupButtonLayout()
		{ }
	void Adorn(VObject *acv, const Rectangle &r, int code);
};

void ETPopupButtonLayout::Adorn(VObject *acv, const Rectangle&, int)
{
	Rectangle rr(acv->contentRect);
	rr.extent-= gPoint1;
	rr.origin+= gPoint1;
	GrPaintLine(gET->shadowColor, 1, eDefaultCap, rr.SW(), rr.SE());
	GrPaintLine(gET->shadowColor, 1, eDefaultCap, rr.NE(), rr.SE());
	rr.origin-= gPoint1;
	GrStrokeRect(rr);
}

//---- ETSliderLayout ----------------------------------------------------------

class ETSliderLayout : public Layout {
public:
	ETSliderLayout()
		{ }
	void Adorn(VObject *acv, const Rectangle &r, int code);
};

void ETSliderLayout::Adorn(VObject*, const Rectangle &r, int)
{
	GrPaintRect(r, ePatGrey12);
	GrSetPenNormal();
	GrStrokeRect(r);
}

//---- ETThumbLayout -----------------------------------------------------------

class ETThumbLayout : public Layout {
public:
	ETThumbLayout()
		{ }
	Metric GetMinSize(VObject*)
		{ return Metric(20); }
	void Adorn(VObject *acv, const Rectangle &r, int code);
};

void ETThumbLayout::Adorn(VObject*, const Rectangle &r, int)
{
	GrPaintRect(r, ePatGrey50);
	GrStrokeRect(r);
}

//---- ETFieldBorderLayout --------------------------------------------------

class ETFieldBorderLayout : public Layout {
	bool noborder;
public:
	ETFieldBorderLayout(bool nb)
		{ noborder= nb; }
	void Adorn(VObject *acv, const Rectangle &r, int code);
	long GetValue(VObject*, int)
		{ return noborder ? 1 : 2; }
};

void ETFieldBorderLayout::Adorn(VObject *acv, const Rectangle&, int code)
{
	int bw= 1;
	if (noborder) {
		if (!code)
			bw= 0;
	} else {
		if (code)
			bw= 2;
	}
	if (bw > 0)
		GrGetPort()->StrokeRect(gInkBlack, bw, acv->contentRect);
}

//---- ETMenuBarLayout ---------------------------------------------------------

class ETMenuBarLayout : public Layout {
public:
	ETMenuBarLayout()
		{ }
	Metric GetMinSize(VObject*);
	void SetOrigin(VObject *acv, const Point &at)
		{ acv->At(0)->SetOrigin(at+gPoint1); }
	void SetExtent(VObject *acv, const Point &e)
		{ acv->At(0)->SetExtent(e-gPoint2); }
	void Adorn(VObject *acv, const Rectangle&, int)
		{ GrStrokeRect(acv->contentRect); }
};

Metric ETMenuBarLayout::GetMinSize(VObject *acv)
{
	Metric m(acv->At(0)->GetMinSize());
	m.extent+= gPoint2;
	m.base+= 1;
	return m;
}

//---- ETSashLayout ------------------------------------------------------------

class ETSashLayout : public Layout {
public:
	ETSashLayout()
		{ }
	Metric GetMinSize(VObject*)
		{ return gPoint3; }
	void Adorn(VObject *acv, const Rectangle &r, int code);
};

void ETSashLayout::Adorn(VObject *acv, const Rectangle&, int)
{
	Point p1, p2;
	Rectangle rr(acv->contentRect);
	if (rr.extent.x > rr.extent.y) { // horizontal
		p1= rr.W();
		p2= rr.E();
		//p1.y--;
		//p2.y--;
		rr.origin.x+= rr.extent.x - 20;
		rr.extent.x= 20;
	} else {    // vertical
		p1= rr.N();
		p2= rr.S();
		//p1.x--;
		//p2.x--;
		rr.origin.y+= rr.extent.y - 20;
		rr.extent.y= 20;
	}
	GrLine(p1, p2);
	GrPaintRect(rr, gInkBlack);
}

//---- ETProgressBarLayout -----------------------------------------------------

class ETProgressBarLayout : public Layout {
public:
	ETProgressBarLayout()
		{ }
	Metric GetMinSize(VObject*)
		{ return Metric(200, 20); }
	void Adorn(VObject *acv, const Rectangle &r, int code);
};

void ETProgressBarLayout::Adorn(VObject *acv, const Rectangle&, int val)
{
	Rectangle rr(acv->contentRect);
	rr.extent.x= val;
	GrPaintRect(rr, ePatGrey50);
	GrStrokeRect(acv->contentRect);
}

//---- ETScaleLayout -----------------------------------------------------------

class ETScaleLayout : public Layout {
public:
	ETScaleLayout()
		{ }
	Metric GetMinSize(VObject *acv)
		{ return acv->At(0)->GetMinSize(); }
	void Adorn(VObject *acv, const Rectangle &r, int code);
};

void ETScaleLayout::Adorn(VObject *acv, const Rectangle&, int)
{
	Rectangle cr= acv->contentRect;
	GrStrokeRect(cr);
}

//---- ETFrameLayout -----------------------------------------------------------

class ETFrameLayout : public Layout {
public:
	ETFrameLayout()
		{ }
	void Adorn(VObject *acv, const Rectangle &r, int code);
	long GetValue(VObject*, int code)
		{ return code ? -1 : 1; }
};

void ETFrameLayout::Adorn(VObject*, const Rectangle &r, int)
{
	GrStrokeRect(r.Expand(gPoint1));
}

//---- ETScaleThumbLayout ------------------------------------------------------

class ETScaleThumbLayout : public Layout {
	Direction dir;
public:
	ETScaleThumbLayout(Direction d)
		{ dir= d; }
	Metric GetMinSize(VObject *acv);
	void Adorn(VObject *acv, const Rectangle &r, int code);
};

Metric ETScaleThumbLayout::GetMinSize(VObject*)
{
	if (dir == eHor)
		return Metric(20, 15);
	return Metric(15, 20);
}

void ETScaleThumbLayout::Adorn(VObject *acv, const Rectangle&, int)
{
	GrPaintRect(acv->contentRect, ePatGrey50);
	GrStrokeRect(acv->contentRect);
}

//---- ETGroupLayout -----------------------------------------------------------

class ETGroupLayout : public Layout {
	Point border;
	int cBorderTitleGap;
public:
	ETGroupLayout()
		{ border= gPoint6; cBorderTitleGap= 4; }
	void SetOrigin(VObject*, const Point&);
	void SetExtent(VObject*, const Point&);
	Metric GetMinSize(VObject *acv);
	void Adorn(VObject *acv, const Rectangle &r, int code);
};

Metric ETGroupLayout::GetMinSize(VObject *acv)
{
	VObject *interior= acv->At(0);
	Metric m= interior->GetMinSize();

	if (acv->Size() > 1) {
		VObject *titleBar= acv->At(1);
		Metric tm= titleBar->GetMinSize();
		m.extent.x= Math::Max(m.extent.x, tm.extent.x+2*cBorderTitleGap);
		int d= tm.extent.y - border.y;
		if (d > 0) {
			m.extent.y+= d;
			m.base+= d;
		}
	}
	return m.Expand(border);
}

void ETGroupLayout::SetExtent(VObject *acv, const Point &ee)
{
	Point e(ee);
	if (acv->Size() > 1) {
		VObject *titleBar= acv->At(1);
		titleBar->CalcExtent();
		int d= titleBar->Height() - border.y;
		if (d > 0)
			e.y-= d;
	}
	acv->At(0)->SetExtent(e-(2*border));
}

void ETGroupLayout::SetOrigin(VObject *acv, const Point &a)
{
	Point at(a);
	if (acv->Size() > 1) {
		VObject *titleBar= acv->At(1);
		int th= titleBar->Height();
		int tgap= border.x+cBorderTitleGap;
		titleBar->Align(Point(at.x+tgap, at.y), Metric(acv->Width()-2*tgap, th),
								(VObjAlign)((eVObjHLeft & eVObjH) | eVObjVTop));
		int d= th - border.y;
		if (d > 0)
			at.y+= d;
	}
	acv->At(0)->SetOrigin(at+border);
}

void ETGroupLayout::Adorn(VObject *acv, const Rectangle &r, int)
{
	if (! acv->At(0)->contentRect.ContainsRect(r)) {
		if (acv->Size() > 1) {
			VObject *titleBar= acv->At(1);
			int h= titleBar->Height()/2-1;
			Rectangle rr(acv->contentRect);
			rr.origin.y+= h;
			rr.extent.y-= h;

			GrLine(Point(titleBar->GetOrigin().x, rr.origin.y), rr.NW());
			GrLine(rr.NW(), rr.SW());
			GrLine(rr.SW(), rr.SE());
			GrLine(rr.SE(), rr.NE());
			GrLine(rr.NE(), Point(titleBar->contentRect.NE().x, rr.origin.y));
		} else
			GrStrokeRect(acv->contentRect);
	}
}

//---- ET++ --------------------------------------------------------------------

ETLook::ETLook()
{
	gET= this;

	if (gDepth > 1) {
		shadowColor= new_Grey(0.5);
		menuTitleColor= new_Grey(230);
		windowHighlightColor= new_Grey(0.8);
	} else {
		shadowColor= ePatGrey50;
		menuTitleColor= gInkXor;
		windowHighlightColor= gInkXor;
	}

	arrows[0]= new Bitmap(16, UpArrowBits);
	arrows[1]= new Bitmap(16, DownArrowBits);
	arrows[2]= new Bitmap(16, LeftArrowBits);
	arrows[3]= new Bitmap(16, RightArrowBits);
	invarrows[0]= new Bitmap(16, InvUpArrowBits);
	invarrows[1]= new Bitmap(16, InvDownArrowBits);
	invarrows[2]= new Bitmap(16, InvLeftArrowBits);
	invarrows[3]= new Bitmap(16, InvRightArrowBits);
	menuArrow= new Bitmap(11, menuArrowBits);

	Point s(14, 13);
	radioButtonOn= new Bitmap(s, RadioButtonOnBits);
	radioButtonOff= new Bitmap(s, RadioButtonOffBits);
	radioButtonFeedback= new Bitmap(s, RadioButtonFeedbackBits);

	CheckMark= new Bitmap(14, 12, CheckMarkBits);

	CheckMarkOn= new Bitmap(s, CheckMarkOnBits);
	CheckMarkOff= new Bitmap(s, CheckMarkOffBits);
	CheckMarkFeedback= new Bitmap(s, CheckMarkFeedbackBits);

	SliderUp= new Bitmap(13, 10, SliderUpBits);
	SliderDown= new Bitmap(13, 10, SliderDownBits);
	SliderUpInv= new Bitmap(13, 10, SliderUpInvBits);
	SliderDownInv= new Bitmap(13, 10, SliderDownInvBits);

	popUpMenuLayout= new ETMenuBorderLayout(5);
	pullDownMenuLayout= new ETMenuBorderLayout(1);
	menuLineLayout= new ETMenuLineLayout;
	menuItemLayout= new ETMenuItemLayout;
	scrollButtonLayout= new ETScrollButtonLayout;

	toggleButtonLayout= new ETToggleButtonLayout(CheckMarkOn, CheckMarkOff, CheckMarkFeedback);
	radioButtonLayout= new ETToggleButtonLayout(radioButtonOn, radioButtonOff, radioButtonFeedback);
	scrollBarLayout= new ETScrollBarLayout;
	actionButtonLayout= new ETActionButtonLayout(FALSE);
	defaultButtonLayout= new ETActionButtonLayout(TRUE);
	popupButtonLayout= new ETPopupButtonLayout;
	sliderLayout= new ETSliderLayout;
	thumbLayout= new ETThumbLayout;
	fieldBorderLayout= new ETFieldBorderLayout(FALSE);
	fieldLayout= new ETFieldBorderLayout(TRUE);
	menuBarLayout= new ETMenuBarLayout;
	sashLayout= new ETSashLayout;
	progressBarLayout= new ETProgressBarLayout;
	scaleLayout= new ETScaleLayout;
	scaleThumbLayout[eHor]= new ETScaleThumbLayout(eHor);
	scaleThumbLayout[eVert]= new ETScaleThumbLayout(eVert);
	groupLayout= new ETGroupLayout;
	frameLayout= new ETFrameLayout;
}

ETLook::~ETLook()
{
	for (int i= 0; i < 4; i++) {
		SafeDelete(arrows[i]);
		SafeDelete(invarrows[i]);
	}
	SafeDelete(menuArrow);
	SafeDelete(radioButtonOn);
	SafeDelete(radioButtonOff);
	SafeDelete(radioButtonFeedback);
	SafeDelete(CheckMarkOn);
	SafeDelete(CheckMarkOff);
	SafeDelete(CheckMarkFeedback);
	SafeDelete(CheckMark);
	SafeDelete(SliderUp);
	SafeDelete(SliderDown);
	SafeDelete(SliderUpInv);
	SafeDelete(SliderDownInv);
}

void ETLook::DrawHighlight(Rectangle &r)
{
	GrPaintRect(r, gHighlightColor);
}


